#include <linux/cpu.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/export.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/kernel_stat.h>
#include <linux/kthread.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/percpu.h>
#include <linux/sched.h>
#include <linux/sched/task.h>
#include <linux/slab.h>
#include <linux/smp.h>
#include <linux/smpboot.h>
#include <linux/time.h>

/************************************************************************************
 *
 *          测试点
 *
 *	线程亲和度 	  绑核问题
 *	smp 	 	mb问题 rmb smb_rmb
 *	smp      	锁问题
 *	barrier  	优化屏障问题
 *	cpu      	位图问题
 *   per-cpu     每cpu变量测试
 *	cpu state	状态查询、设置问题
 *	smp         Cache多核同步问题
 *   单核         数据和指令Cache问题
 *
 *
 * 创建线程A 绑定到CPU0，      线程B绑定到CPU1
 * 对变量a进行多核同步操作测试
 *
 *
 *
 *************************************************************************************/

struct task_struct *kthread[NR_CPUS] = {NULL};

static uint32_t a, b;

static int kthread_on_cpu1(void *arg)
{
  set_current_state(TASK_UNINTERRUPTIBLE);

  while (!kthread_should_stop()) {
    a = 1;
    smp_wmb();
    b = 1;
  }
}

// 在loop中没有让出cpu的操作，需要关掉内核抢占，否则引起rcu卡死问题
static int kthread_on_cpu2(void *arg)
{
  set_current_state(TASK_UNINTERRUPTIBLE);

  while (!kthread_should_stop()) {
    while (b == 0)
        continue;
    smp_rmb();
    BUG_ON(a == 0);
    a = 0;
    smp_wmb();
    b = 0;
    smp_wmb();
  }

}

static void self_test(void) {
  unsigned int cpu;

  //nr_cpu_ids
  for_each_online_cpu(cpu) { pr_info("cpu%d is online\n", cpu); }

  /* ktread 1 on cpu 0*/
  cpu = 1;
  kthread[cpu] =
      kthread_create_on_cpu(kthread_on_cpu1, (void *)&cpu, cpu, "kthread/%d");
  if (IS_ERR(kthread[cpu])) {
    pr_err("fail to kthread_create_on_cpu\n");
    return;
  }

  cpu = 2;
  kthread[cpu] =
      kthread_create_on_cpu(kthread_on_cpu2, (void *)&cpu, cpu, "kthread/%d");
  if (IS_ERR(kthread[cpu])) {
    pr_err("fail to kthread_create_on_cpu\n");
    return;
  }

  wake_up_process(kthread[1]);
  wake_up_process(kthread[2]);
}

static int __init test_smp_init(void) {
  pr_info("kenel module init\n");

  self_test();

  return 0;
}

static void __exit test_smp_exit(void) 
{ 
    pr_info("kernel module exit\n");
    kthread_stop(kthread[1]);
    kthread[1] = NULL;
    kthread_stop(kthread[2]);
    kthread[2] = NULL;
}

module_init(test_smp_init);
module_exit(test_smp_exit);

MODULE_DESCRIPTION("hello - Linux Lab module example");
MODULE_AUTHOR("jiangxiujie <one_face@sina.com>");
MODULE_LICENSE("GPL");
